// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of luhn
//
// luhn is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// luhn is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with DOM3ImplReg. If not, see <http://www.gnu.org/licenses/>.
//	Copyright (c) 2008, Sidewinder ltd.
//
//   Purpose: checks luhn ids.
//   $Id$
//	 Notes:
//		See http://en.wikipedia.org/wiki/Luhn for a description of luhn numbers.


#include <string>
#include <luhn.h>

CLuhnIDChecker::CLuhnIDChecker()
	:m_sIgnoreList(0)
{
}

CLuhnIDChecker::~CLuhnIDChecker()
{
	delete [] m_sIgnoreList;
}

void CLuhnIDChecker::put_ignoreList(const wchar_t * sIgnoreList)
{
	//Clear the old ignore list, create a new one, and copy in the supplied string.
	delete [] m_sIgnoreList;
	m_sIgnoreList = new wchar_t[ wcslen(sIgnoreList)+1];
	wcscpy(m_sIgnoreList,sIgnoreList);
}

void CLuhnIDChecker::clear_ignoreList()
{
	delete [] m_sIgnoreList;
	m_sIgnoreList = 0;
}

bool CLuhnIDChecker::checkLuhnID(const wchar_t * id) const
{
	return checkLuhnID_l(id,static_cast<int>(wcslen(id)));
}

bool CLuhnIDChecker::checkLuhnID_l(const wchar_t *id,int length) const
{
	//bToggle is used to alternate between the two modes of computation of the value to sum.  
    bool bToggle = false;

	int iSum = 0;
	//sum all the digits, starting at the end, and doubling every alternate digit.
    for (int l = length-1; l >=0; --l) 
	{
		const wchar_t* c = &id[l];
		//If the current character is outside the bounds of decimal digits
		//	then complain.
		if(*c < L'0' || *c > L'9')
		{
			if(!m_sIgnoreList || wcschr(m_sIgnoreList,*c) == 0)
				return false;
			else
				continue;
		}

		//The numerical value of a character is 
		//	that of its char value - the char value of 
		//	0.
		int digit = *c - L'0';
        
		//check if this digit is one that requires computation
		// from the initial definition of bToggle, you will see
		//	that if len is odd, then this will be true to start
		
		if (bToggle) 
		{
			//in the case that this digit is one of the 
			//	alternates that require doubling, do so.
            digit *= 2;
            if (digit > 9)
				//subtract 9 to reduce it to one digit
				digit -= 9;
        }

        //add the computed value of this digit
		iSum += digit;

		//flipflop the toggle
        bToggle = !bToggle;
	}
	//Check that the resulting sum is a multiple of 10.
	
	return !(iSum % 10);
}